/*
 * klog.h
 * Author: KevinKW
 * Define the internal interface of klog
 *
 */

#ifndef __KLOG_HPP__
#define __KLOG_HPP__

#include <pthread.h>
#include <stdio.h>
#include <queue>
#include <time.h>
#include <assert.h>

#include "klog.h"

using namespace std;

namespace KLOG {
#define BUFSIZE 512
#define LOGLINE_LEN 256
#define LOGPATH "/var/log"
#define FILENAME "klog.log"

    class kLog {
      public:
        kLog() {
            strcpy(logPath, LOGPATH);
            strcpy(fileName, FILENAME);
            fileNameLen = strlen(fileName);
            init = false;
            logFile = NULL;
            addHostName = true;
            addTimeStamp = true;
            addSeqNum = false;
            seqNum = 1;
            logLevel =  LogInfo;
            fileSize = 2 * 1024 * 1024; // 2M file
            logFileNum = 1; //
            maxLogFileNum = 5; //
            pthread_mutex_init(&mutex, NULL);
            deleteFileNames();
        }
        ~kLog() {
            if (logFile) {
                fclose(logFile);
            }

            deleteFileNames();
        }
        void lock() { pthread_mutex_lock(&mutex); }
        void unlock() { pthread_mutex_unlock(&mutex); }
        bool isLocked() { return pthread_mutex_trylock(&mutex) == EBUSY; }
        void updateFile();
        bool setLogFile(char* path, char* fname);
        void getLogFile(char* path, char* fname) {
            lock();
            if (path)
                snprintf(path, PATHLEN, "%s", logPath);
            if (fname)
                snprintf(fname, PATHLEN, "%s", fileName);
            unlock();
        }
        void setLogFileSize(unsigned long long size) {
            lock();
            fileSize = size;
            unlock();
        }
        unsigned long long getLogFileSize() {
            unsigned long long size;
            lock();
            size = getLogFileSizeLocked();
            unlock();
            return size;
        }
        void setMaxLogFileNum(unsigned int logFileNum) {
            lock();
            maxLogFileNum = logFileNum;
            deleteFileNames();
            buildFileNameList();
            unlock();
        }
        unsigned int getMaxLogFileNum() {
            int num;
            lock();
            num = getMaxLogFileNumLocked();
            unlock();
            return num;
        }
        void setLogLevel(kLogLevel level) {
            lock();
            logLevel = level;
            unlock();
        }
        kLogLevel getLogLevel() {
            kLogLevel log;
            lock();
            log = getLogLevelLocked();
            unlock();
            return log;
        }
        void addHostname(bool isAddHostName) {
            lock();
            addHostName = isAddHostName;
            unlock();
        }
        bool isAddHostname() {
            bool ret;
            lock();
            ret = isAddHostnameLocked();
            unlock();
            return ret;
        }
        void addTime(bool isAddTime) {
            lock();
            addTimeStamp = isAddTime;
            unlock();
        }
        bool isAddTime() {
            bool addTime;
            lock();
            addTime = isAddTimeLocked();
            unlock();
            return addTime;
        }
        void addSeqId(bool addId) {
            lock();
            addSeqNum = addId;
            seqNum = 1;
            unlock();
        }
        bool isAddSeqId() {
            bool addSeqId;
            lock();
            addSeqId = isAddSeqIdLocked();
            unlock();
            return addSeqId;
        }
        void klog(kLogLevel logLevel, const char* logFormat, va_list args); 

      private:
        unsigned long long getLogFileSizeLocked() { return fileSize; }
        unsigned int getMaxLogFileNumLocked() { return maxLogFileNum; }
        kLogLevel getLogLevelLocked() { return logLevel; }
        bool isAddTimeLocked() { return addTimeStamp; }
        bool isAddHostnameLocked() { return addHostName; }
        bool isAddSeqIdLocked() { return addSeqNum; }
        void getFileName(char* name);
        void deleteFileNames() {
            while (!fileNames.empty()) {
                char* tmp = fileNames.front();
                delete [] tmp;
                fileNames.pop();
            }
        }
        bool initFile();
        bool buildFileNameList();
        bool closeFile() {
            assert(isLocked());
            if (init) {
                fclose(logFile);
            }
            init = false;
            logFile = NULL;
            logSize = 0;
            return true;
        }
        bool renameFile();

      private:
        char logPath[PATHLEN];
        char fileName[PATHLEN];
        unsigned int fileNameLen;
        queue<char*> fileNames;
        FILE* logFile;
        char logBuf[BUFSIZE];
        bool init;
        bool addHostName;
        bool addTimeStamp;
        bool addSeqNum;
        unsigned long long seqNum;
        kLogLevel logLevel;
        unsigned long long fileSize;
        unsigned long long logSize;
        unsigned int maxLogFileNum;
        unsigned int logFileNum;
        pthread_mutex_t mutex;
    };
}
#endif // __KLOG_HPP__
